package vn.bang.xiangqi.server;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;

import asia.redact.bracket.properties.Properties;

import vn.bang.communication.Command;
import vn.bang.communication.Communicator;
import vn.bang.entity.GameDTO;
import vn.bang.entity.PlayerDTO;
import vn.bang.xiangqi.server.log.Log;
import vn.bang.xiangqi.server.thread.CommandProcessorThread;

public class ServerManager {

	private Properties props = getConfigProperties();
	private final Queue<Command> commandQueue = new LinkedList<Command>();
	private final Collection<PlayerDTO> players = new ArrayList<PlayerDTO>();
	private final Collection<GameDTO> games = new ArrayList<GameDTO>();

	// EX: port(key) -> [playerId(key) -> communicator(value)](value)
	private Map<Integer, Map<Long, Communicator>> communicators;

	private CommandProcessorThread commandProcessorThread;
	private LoginServer loginServer;
	private XiangqiServer xiangqiServer;

	public CommandProcessorThread getCommandProcessorThread() {
		return commandProcessorThread;
	}
	
	public LoginServer getLoginServer() {
		return loginServer;
	}
	
	public XiangqiServer getXiangqiServer() {
		return xiangqiServer;
	}
	
	public static void main(String[] args) {
		ServerManager serverManager = new ServerManager();
		serverManager.startup();
	}

	public ServerManager() {
		initCommunicators();
		initCommandProcessorThread();
		initLoginServer(null != props ? props.intValue(AppResource.LOGIN_SERVER_THREAD_NUMBER) : 1);
		initXiangqiServer(null != props ? props.intValue(AppResource.LOGIN_SERVER_THREAD_NUMBER) : 1);
	}

	public void startup() {
		Log.info(String.format("Started login server with %d thread(s)", loginServer.getNumberOfThreads()));
		startupServer(loginServer);
		
//		ServerLog.info(String.format("Started xiangqi server with %d thread(s)", xiangqiServer.getNumberOfThreads()));
//		startupServer(xiangqiServer);
		Log.info("Started the command processor");
		startCommandProcessor(commandProcessorThread);
	}

	public void shutdown() {
		Log.info("Stopped the command processor");
		stopCommandProcessor(commandProcessorThread);
//		ServerLog.info("Stopped xiangqi server");
//		shutdownServer(xiangqiServer);
		Log.info("Stopped login server");
		shutdownServer(loginServer);
	}

	private void initXiangqiServer(int numberOfThreads) {
		int number = numberOfThreads;
		
		if (numberOfThreads < 1) {
			number = 1;
		}

		xiangqiServer = new XiangqiServer(commandQueue,
				communicators.get(ServerPort.GAME_PLAY.getValue()), players,
				games, number);
	}

	private void initLoginServer(int numberOfThreads) {
		int number = numberOfThreads;
		
		if (numberOfThreads < 1) {
			number = 1;
		}

		loginServer = new LoginServer(commandQueue,
				communicators.get(ServerPort.LOGIN.getValue()), players, games,
				number);
	}

	private void initCommandProcessorThread() {
		commandProcessorThread = new CommandProcessorThread(commandQueue,
				communicators, games, players);
	}

	private void initCommunicators() {
		communicators = new HashMap<Integer, Map<Long, Communicator>>();
		
		for (ServerPort portEnum : ServerPort.values()) {
			Integer port = portEnum.getValue();
			communicators.put(port, new HashMap<Long, Communicator>());
		}
	}

	private Properties getConfigProperties() {
		Properties props = null;
		try {
			Reader reader = new FileReader(AppResource.SERVER_CONFIG_FILE_NAME);
			props = Properties.Factory.getInstance(reader);
		} catch (FileNotFoundException e) {
			Log.warn("File not found.", e);
		}
		return props;
	}
	
	private void startupServer(IServer server) {
		server.startup();
	}

	private void shutdownServer(IServer server) {
		server.shutdown();
	}

	private void startCommandProcessor(CommandProcessorThread commandProcessorThread) {
		commandProcessorThread.start();
	}

	private void stopCommandProcessor(CommandProcessorThread commandProcessorThread) {
		commandProcessorThread.shutdown();
	}

}
